package com.jingdianjichi.wx.controller;

import com.jingdianjichi.wx.handler.WxChatMsgHandler;
import com.jingdianjichi.wx.handler.factory.WxChatMsgFactory;
import com.jingdianjichi.wx.utils.MessageUtil;
import com.jingdianjichi.wx.utils.SHA1;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Objects;

/**
 * 专门用于接受微信的回调
 */
@RestController
@Slf4j
public class CallBackController {

    @RequestMapping("/test")
    public String test(){
        return "hello wx";
    }

    @Resource
    private WxChatMsgFactory wxChatMsgFactory;


    //和微信约定的Token
    private static final String token = "vivo123";

    /**
     * 就是在微信在调用我其他接口之前,会来先调这个接口看看我们的服务器是否合法
     * 接受微信的回调,进行验签验签通过后微信才会给你返回码用户的信息
     * ture对请求进行校验（下面有校验方式）。若确认此次GET请求来自微信服务器，请原样返回echostr参数内容，则接入生效，成为开发者成功，否则接入失败。加密/校验流程如下：
     * 1）将token、timestamp、nonce三个参数进行字典序排序
     * 2）将三个参数字符串拼接成一个字符串进行sha1加密
     * 3）开发者获得加密后的字符串可与signature对比，标识该请求来源于微信
     * @param signature 微信加密签名，signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
     * @param timestamp 时间戳
     * @param nonce     随机数
     * @param echostr   随机字符串
     * @return
     */
    @GetMapping("/callback")
    public String callBack(@RequestParam("signature") String signature,
                           @RequestParam("timestamp")String timestamp,
                           @RequestParam("nonce")String nonce,
                           @RequestParam("echostr")String echostr){
        log.info("微信回调的验签的请求参数:signature{},timestamp:{},nonce:{},echostr:{}",signature,timestamp,nonce,echostr);

        //因为微信那边我们使用的是密文模式来的,但是我们先明文,否则不好调试
        //SHA1加密
        String sha1Str = SHA1.getSHA1(token, timestamp, nonce, "");

        //一样的就是验签成功,就是我们的Token是一样的,微信就信任你了亲
        if (signature.equals(sha1Str)){
            return echostr;
        }

        //失败
        return "unknown";
    }


    /**
        用户关注我们之后就会调用这个回调地址通知我们,但是是 post的格式给你的,但是微信给我们推的这个都是xml形式的我们要设置一下,不能按照平常的json来接收板了
        用户网公众号发消息的时候也是这个接口回调的,我们可以接受文本语音,视频的消息都可以的
        <xml>
        <ToUserName><![CDATA[gh_7a7aff2cb3d0]]></ToUserName>                    //这个就是我们的公众号的唯一id
        <FromUserName><![CDATA[oBQ7w7EfO7H1aWVb4rWsYDDuV5hc]]></FromUserName>   //这个就是你的微信号在当前这个公众号的唯一id,我们就可以去把他当中用户的账号了唯一id
        <CreateTime>1739728411</CreateTime>                                     //创建的时间
        <MsgType><![CDATA[event]]></MsgType>                                    //关注的事件
        <Event><![CDATA[subscribe]]></Event>                                    //表示我们是一个关注的操作
        <EventKey><![CDATA[]]></EventKey>
        </xml>
     */
    /*
        @PostMapping(value = "/callback",produces = "application/xml;charset=UTF-8")
        public String callBack(
                @RequestBody String requestBody,                    //微信直接把所有的消息体放到这里
                @RequestParam("signature") String signature,        //微信加密签名
                @RequestParam("timestamp")String timestamp,         //时间戳
                @RequestParam("nonce")String nonce,                 //随机数
                @RequestParam(value = "msg.signature",required = false) String msgSignature         //以为我们现在是明文模式来的,他是不会传递这个的,如果是密文的话就传递的
                ){

            log.info("接收到微信的请求:requestBody{},signature:{},timestamp:{},nonce:{}",requestBody,signature,timestamp,nonce);

            //我们返回我们系统的验证码,但是也是xml形式的,告诉微信,你发给谁(微信的用户),来自哪里(公众号)

            //解析XML
            Map<String, String> map = MessageUtil.parseXml(requestBody);
            String toUserName = map.get("ToUserName");          //发给谁的,公众号
            String fromUserName = map.get("FromUserName");      //谁发的.微信用户

            //我们是5s内自动回复消息的,所以我们这里使用微信的被动回复用户的消息,这里也是返回微信xml格式的
            String msg="<xml>\n" +
                    "  <ToUserName><![CDATA["+fromUserName+"]]></ToUserName>\n" +
                    "  <FromUserName><![CDATA["+toUserName+"]]></FromUserName>\n" +
                    "  <CreateTime>12345678</CreateTime>\n" +
                    "  <MsgType><![CDATA[text]]></MsgType>\n" +
                    "  <Content><![CDATA[你好,我是经典鸡翅,欢迎学习!!]]></Content>\n" +
                    "</xml>";

            return msg;
        }
     */

    /**
     * 但是现在的问题是,我们的把这个回复的东西解耦,我们不能在这里写
     *  /我们可以整一个策略模式,根据发送的东西做自身业务的处理
     *      我们看消息体MsgType这个是不同的,如果用户是关注的话,就是事件来的,如果是发送消息的话就是
     *      /text类型,我们可以根据这个来进行策略模式,区分不同的消息
     *      /还是使用工厂+策略的模式来处理不同的消息,因为我们现在的就是不管用户干啥我们都返回同样的消息的
     */


    /**
     * 使用工厂+策略的模式实现消息的解耦
     */
    /*
         接受用户消息的消息体
            <xml>
              <ToUserName><![CDATA[toUser]]></ToUserName>
              <FromUserName><![CDATA[fromUser]]></FromUserName>
              <CreateTime>1348831860</CreateTime>
              <MsgType><![CDATA[text]]></MsgType>
              <Content><![CDATA[this is a test]]></Content>
              <MsgId>1234567890123456</MsgId>
              <MsgDataId>xxxx</MsgDataId>
              <Idx>xxxx</Idx>
            </xml>

         公众号关注的消息体
           <xml>
              <ToUserName><![CDATA[toUser]]></ToUserName>
              <FromUserName><![CDATA[FromUser]]></FromUserName>
              <CreateTime>123456789</CreateTime>
              <MsgType><![CDATA[event]]></MsgType>
              <Event><![CDATA[subscribe]]></Event>
            </xml>

     */
    @PostMapping(value = "/callback",produces = "application/xml;charset=UTF-8")
    public String callBack(
            @RequestBody String requestBody,                                             //微信直接把所有的消息体放到这里
            @RequestParam("signature") String signature,                                 //微信加密签名
            @RequestParam("timestamp")String timestamp,                                  //时间戳
            @RequestParam("nonce")String nonce,                                          //随机数
            @RequestParam(value = "msg.signature",required = false) String msgSignature  //以为我们现在是明文模式来的,他是不会传递这个的,如果是密文的话就传递的
    ){

        log.info("接收到微信的请求:requestBody{},signature:{},timestamp:{},nonce:{}",requestBody,signature,timestamp,nonce);

        //工厂+策略回复用户消息
        Map<String, String> messageMap = MessageUtil.parseXml(requestBody);

        String msgType = messageMap.get("MsgType");
        String event = messageMap.get("Event") == null ? "" : messageMap.get("Event");      //以为对于事件来说不是每次请求都有的

        log.info("msgType:{},event:{}",msgType,event);

        StringBuilder sb=new StringBuilder();

        sb.append(msgType);

        //拼接字符串
        if (!StringUtils.isEmpty(event)){
            sb.append(".");
            sb.append(event);
        }

        String msgTypeKey=sb.toString();

        //获得消息体
        WxChatMsgHandler wxChatMsgHandler = wxChatMsgFactory.getHandlerByMsgType(msgTypeKey);

        //我们没有对应的处理器,我们也不知道你发的是啥
        if (Objects.isNull(wxChatMsgHandler)){
            return "unknown";
        }

        //把消息体传过去
        String replyContent = wxChatMsgHandler.dealMsg(messageMap);

        log.info("replyContent:{}",replyContent);

        return replyContent;
    }

}
